﻿namespace NumericalData.Reflection
open System
open System.Reflection
open NumericalData.SolvationMethods
open NumericalData.Attributes
open NumericalData.SolvationMethods.Async
open NumericalData.Systems

type MethodInfoContainer = 
    class
        val m_meth:IterationMethod
        val m_methWorkName:string

        new(meth:IterationMethod) = 
            {
                m_meth = meth
                m_methWorkName = ReflectOps.GetWorkName meth
            }
    end
and
    ReflectOps = 
    class

        static member IsTypeOf (cType:Type) (cObject:obj) = 
            cObject.GetType() = cType

        static member Implements (cTypeName:string) (cObject:obj) = 
            cObject.GetType().GetInterface(cTypeName,false) <> null

        static member GetWorkName (meth:IterationMethod) = 
            let mType = meth.GetType()
            let workNames = 
                mType.GetCustomAttributes(typeof<SolvationMethodAnalysisWorkName>,false) |>
                Array.map (fun attr -> (attr:?>SolvationMethodAnalysisWorkName).AnalysisName)
            if workNames.Length = 0 then
                "Unnamed Method"
            else
                workNames.[0]
            
    end

type SolvationMethodsReflection = 
    class

        static member GetIterationMethods() = 
            let assembly = Assembly.GetAssembly(typeof<TraceableIterationMethod>)
            let iterMethods = 
                assembly.GetExportedTypes() |>
                Array.filter (fun t -> t.BaseType = typeof<TraceableIterationMethod>)
            iterMethods

        static member GetIterationMethods(assembly:Assembly) = 
            let iterMethods = 
                assembly.GetExportedTypes() |>
                Array.filter (fun t -> t.BaseType = typeof<TraceableIterationMethod>)
            iterMethods

        static member GetAsyncableTraceableIterationMethods() = 
            SolvationMethodsReflection.GetIterationMethods() |>
            Array.filter (fun (t:Type) -> t.GetInterface(typeof<IAsyncSolvable<DiagnosticsAnswer>>.Name,false) <> null  )

        static member GetAsyncableTraceableIterationMethodsDictionary() =
            let dict = new System.Collections.Generic.Dictionary<string,IAsyncSolvable<DiagnosticsAnswer>>();
            SolvationMethodsReflection.GetAsyncableTraceableIterationMethods() |>
            Array.iter (
                fun methType ->
                    let meth = methType.GetConstructors().[0].Invoke([||])
                    dict.Add(ReflectOps.GetWorkName(meth:?>IterationMethod), (meth) :?> IAsyncSolvable<DiagnosticsAnswer> )
                )
            dict



    end
type AnswersReflection = 
    class
        static member GetNameValuesDictionary<'T when 'T :> BaseAnswer>(ans:'T)=
            let dict = System.Collections.Generic.Dictionary<string,string>();
            let scanProps:PropertyInfo[] = AnswersReflection.GetScannableProperties(ans.GetType())
            let values = 
                scanProps |>
                Array.map (fun prop -> prop.GetValue(ans,null).ToString())
            let propNames:string[] = scanProps |> AnswersReflection.ExecutePropertiesWorkNames
            for index in 0 .. (propNames.Length - 1) do
                dict.Add(propNames.[index],values.[index])
            dict


        static member GetScannableProperties (ansType:Type) = 
            ansType.GetProperties() |> 
            Array.filter (
                fun prop -> 
                    (prop.CanRead) && (prop.PropertyType.GetInterface("IConvertible",false) <> null )&&
                    ((prop.GetCustomAttributes(false)|>
                        Array.tryFindIndex (ReflectOps.IsTypeOf typeof<PropertyAnalysisWorkName>)) <> None )
                )

        static member ExecutePropertiesWorkNames (properties:PropertyInfo[]) = 
            properties |>
            Array.map (fun prop -> prop.GetCustomAttributes(false))|>
            Array.map (fun attribs ->
                match (attribs |> 
                            Array.tryFind 
                                (ReflectOps.IsTypeOf typeof<PropertyAnalysisWorkName>) 
                      ) with
                |None -> ""
                |Some(name) -> (name :?>PropertyAnalysisWorkName).AnalysisName)
    end

